/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.pool2.impl;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

import org.apache.commons.pool2.BaseObject;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectState;
import org.apache.commons.pool2.SwallowedExceptionListener;

/**
 * 为｛@link GenericObjectPool｝和｛@link GenericKeyedObjectPool}提供通用功能的基类
 * 。这个类存在的主要原因是减少了两个池实现之间的代码重复。这个类的具体实现应该是线程安全的。
 */
public abstract class BaseGenericObjectPool<T> extends BaseObject implements AutoCloseable {


    /**
     * 空闲对象驱逐迭代器。保留对空闲对象的引用。
     * <p>
     * 用于在对象池中进行对象的驱逐操作，当对象池中的某个对象不再需要时，可以使用EvictionIterator来遍历对象池中的所有对象，并逐个进行驱逐操作。
     * 具体来说，EvictionIterator会按照一定的规则（例如FIFO、LIFO等）来遍历对象池中的对象，并对每个对象执行相应的驱逐逻辑。
     * <p>
     * 由于EvictionIterator只是一个迭代器类，因此在使用之前需要先获取到对象池的实例，并通过调用其getEvictionIterator方法来获取EvictionIterator对象。
     */
    final class EvictionIterator implements Iterator<PooledObject<T>> {

        private final Deque<PooledObject<T>> idleObjects;
        private final Iterator<PooledObject<T>> idleObjectIterator;

        /**
         * 为提供的空闲实例deque构造一个EvctionIterator。
         */
        EvictionIterator(final Deque<PooledObject<T>> idleObjects) {
            this.idleObjects = idleObjects;

            if (getLifo()) {
                idleObjectIterator = idleObjects.descendingIterator();
            } else {
                idleObjectIterator = idleObjects.iterator();
            }
        }

        /**
         * 获取此迭代器引用的空闲对象deque
         *
         * @return the idle object deque
         */
        public Deque<PooledObject<T>> getIdleObjects() {
            return idleObjects;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean hasNext() {
            return idleObjectIterator.hasNext();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public PooledObject<T> next() {
            return idleObjectIterator.next();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void remove() {
            idleObjectIterator.remove();
        }

    }


    /**
     * 空闲对象的驱逐者"定时任务，继承自{@link TimerTask}
     *
     * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
     */
    final class Evictor implements Runnable {

        private ScheduledFuture<?> scheduledFuture;

        /**
         * Cancels the scheduled future.
         */
        void cancel() {
            scheduledFuture.cancel(false);
        }

        BaseGenericObjectPool<T> owner() {
            return BaseGenericObjectPool.this;
        }

        /**
         * 运行对象池维护线程。
         * 驱逐对象具有驱逐者的资格，同时保证空闲实例可用的最小数量。
         * 因为调用"驱逐者线程"的定时器是被所有对象池共享的，
         * 但对象池可能存在不同的类加载器中，所以驱逐者必须确保采取的任何行为
         * 都得在与对象池相关的工厂的类加载器下。
         */
        @Override
        public void run() {
            final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader();
            try {
                if (factoryClassLoader != null) {
                    // Set the class loader for the factory
                    final ClassLoader cl = factoryClassLoader.get();
                    if (cl == null) {
                        // The pool has been dereferenced and the class loader
                        // GC'd. Cancel this timer so the pool can be GC'd as
                        // well.
                        cancel();
                        return;
                    }
                    Thread.currentThread().setContextClassLoader(cl);
                }

                // Evict from the pool
                try {
                    evict();
                } catch (final Exception e) {
                    swallowException(e);
                } catch (final OutOfMemoryError oome) {
                    // Log problem but give evictor thread a chance to continue
                    // in case error is recoverable
                    oome.printStackTrace(System.err);
                }
                // Re-create idle instances.
                try {
                    ensureMinIdle();
                } catch (final Exception e) {
                    swallowException(e);
                }
            } finally {
                // Restore the previous CCL
                Thread.currentThread().setContextClassLoader(savedClassLoader);
            }
        }

        /**
         * Sets the scheduled future.
         *
         * @param scheduledFuture the scheduled future.
         */
        void setScheduledFuture(final ScheduledFuture<?> scheduledFuture) {
            this.scheduledFuture = scheduledFuture;
        }

        @Override
        public String toString() {
            return getClass().getName() + " [scheduledFuture=" + scheduledFuture + "]";
        }

    }


    /**
     * 池管理的对象的包装器。
     *
     * @param <T> type of objects in the pool
     */
    static class IdentityWrapper<T> {

        // 包装的对象
        private final T instance;


        public IdentityWrapper(final T instance) {
            this.instance = instance;
        }

        @Override
        @SuppressWarnings("rawtypes")
        public boolean equals(final Object other) {
            return other instanceof IdentityWrapper && ((IdentityWrapper) other).instance == instance;
        }

        /**
         * @return the wrapped object
         */
        public T getObject() {
            return instance;
        }

        @Override
        public int hashCode() {
            return System.identityHashCode(instance);
        }

        @Override
        public String toString() {
            final StringBuilder builder = new StringBuilder();
            builder.append("IdentityWrapper [instance=");
            builder.append(instance);
            builder.append("]");
            return builder.toString();
        }
    }


    /**
     * 负责统计和管理对象池的组件。
     * 它记录了对象的创建、销毁、借用、归还等操作，同时还可以统计对象的使用情况和性能数据等信息。通过StatsStore对象提供的接口，可以方便地查看和管理对象池的状态和性能数据
     */
    private static final class StatsStore {

        private static final int NONE = -1;
        private final AtomicLong[] values;
        private final int size;
        private int index;

        /**
         * 构造一个具有给定缓存大小的新实例
         *
         * @param size 要在缓存中维护的值的数目
         */
        StatsStore(final int size) {
            this.size = size;
            this.values = new AtomicLong[size];
            Arrays.setAll(values, i -> new AtomicLong(NONE));
        }


        void add(final Duration value) {
            add(value.toMillis());
        }


        /**
         * 将值添加到缓存中。如果缓存已满，则其中一个现有值将被新值替换。
         *
         * @param value 要添加到缓存的新值,线程安全的
         */
        synchronized void add(final long value) {
            values[index].set(value);
            index++;
            if (index == size) {
                index = 0;
            }
        }

        /**
         * 获取缓存值的平均值
         *
         * @return 缓存的平均值，截断为长
         */
        public long getMean() {
            double result = 0;
            int counter = 0;
            for (int i = 0; i < size; i++) {
                final long value = values[i].get();
                if (value != NONE) {
                    counter++;
                    result = result * ((counter - 1) / (double) counter) + value / (double) counter;
                }
            }
            return (long) result;
        }

        /**
         * Gets the mean Duration of the cached values.
         *
         * @return the mean Duration of the cache, truncated to long milliseconds of a Duration.
         */
        Duration getMeanDuration() {
            return Duration.ofMillis(getMean());
        }

        /**
         * Gets the current values as a List.
         *
         * @return the current values as a List.
         */
        synchronized List<AtomicLong> getValues() {
            return Arrays.stream(values, 0, index).collect(Collectors.toList());
        }

        @Override
        public String toString() {
            final StringBuilder builder = new StringBuilder();
            builder.append("StatsStore [");
            // Only append what's been filled in.
            builder.append(getValues());
            builder.append("], size=");
            builder.append(size);
            builder.append(", index=");
            builder.append(index);
            builder.append("]");
            return builder.toString();
        }

    }


    // Constants
    /**
     * 用于存储某些属性的历史数据的缓存大小，以便可以计算滚动平均值
     */
    public static final int MEAN_TIMING_STATS_CACHE_SIZE = 100;
    private static final String EVICTION_POLICY_TYPE_NAME = EvictionPolicy.class.getName();
    private static final Duration DEFAULT_REMOVE_ABANDONED_TIMEOUT = Duration.ofSeconds(Integer.MAX_VALUE);


    // Configuration attributes
    // 最大连接数,默认值 -1
    private volatile int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;

    // 当对象池没有空闲对象时，新的获取对象的请求是否阻塞。 默认值true
    // 当 blockWhenExhausted 设置为 true  时，如果对象池中没有可用对象，客户端线程将会被阻塞，直到有对象可用为止。这种模式被称为“阻塞模式”（Blocking Mode）。
    // 当 blockWhenExhausted 设置为 false 时，如果对象池中没有可用对象，客户端线程将不会等待，而是直接抛出异常，表示无法从对象池中获取到对象。这种模式被称为“非阻塞模式”（Non-Blocking Mode）。
    // 通过设置 blockWhenExhausted 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 blockWhenExhausted 设置为 false，
    // 让客户端线程在无法获取到对象时立即抛出异常。而在低并发场景下，为了提高系统吞吐量，可以将 blockWhenExhausted 设置为 true，让客户端线程在无法获取到对象时进行阻塞等待。
    private volatile boolean blockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;


    // 当连接池资源用尽后，调用者获取连接时的最大等待时间。 默认值-1
    // 表示在对象池中没有可用对象时，客户端等待获取对象的最大时间。当有多个线程同时尝试从对象池中借用对象时，这个值可以防止过多的线程长时间阻塞在等待队列上。
    // maxWaitDuration 的值为一个 Duration 类型的对象，表示等待的最长时间。当一个线程尝试从对象池中借用对象时，
    // 如果对象池为空，线程会进入等待队列。maxWaitDuration 用于限制线程在等待队列中的等待时间。
    // 当等待时间超过 maxWaitDuration 时，线程将抛出异常，表示无法从对象池中获取到对象。

    // 通过设置 maxWaitDuration 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 maxWaitDuration 设置为一个较短的时间，让客户端线程在无法获取到对象时立即抛出异常。
    // 在低并发场景下，为了提高系统吞吐量，可以将 maxWaitDuration 设置为一个较长的时间，让客户端线程在无法获取到对象时进行阻塞等待。
    private volatile Duration maxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT;


    // 表示对象池中的对象分配策略。
    private volatile boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO;


    // 表示对象池中的对象分配策略
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。fairness 用于控制这种对象分配策略。
    // "FIFO"（先进先出）：按照对象被创建的顺序进行分配，即最早创建的对象最先被分配出去。
    // "LIFO"（后进先出）：按照对象被创建的逆序进行分配，即最后创建的对象最先被分配出去。
    // "RANDOM"（随机）：随机选择一个可用的对象进行分配。
    // 通过设置 fairness 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 fairness 设置为 "FIFO" 或 "LIFO"，以便及时回收不再使用的对象；
    // 在低并发场景下，为了提高系统吞吐量，可以将 fairness 设置为 "RANDOM"，减少回收操作的次数和时间。
    private final boolean fairness;


    // 表示在创建对象时是否进行测试. 默认值false
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要创建一个新的对象来满足请求
    // 如果设置为 true，则在创建对象时会对对象进行测试，以确定该对象是否可以继续被使用；
    // 如果设置为 false，则不会进行测试。
    // 通过设置 testOnCreate 的值，可以根据实际需求来选择合适的线程处理方式。在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 testOnCreate 设置为 true，以便及时回收不再使用的对象；而在低并发场景下，为了提高系统吞吐量，可以将 testOnCreate 设置为 false，减少回收操作的次数和时间。
    private volatile boolean testOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;


    // 表示在借用对象时是否进行测试。默认值false
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。testOnBorrow 用于控制这种借用过程。
    // 如果设置为 true，则在借用对象时会对对象进行测试，以确定该对象是否可以继续被使用；
    // 如果设置为 false，则不会进行测试。
    // 通过设置 testOnBorrow 的值，可以根据实际需求来选择合适的线程处理方式。在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 testOnBorrow 设置为 true，以便及时回收不再使用的对象；而在低并发场景下，为了提高系统吞吐量，可以将 testOnBorrow 设置为 false，减少回收操作的次数和时间。
    private volatile boolean testOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;


    // 表示在对象被归还到对象池时是否进行测试。 默认值false
    // 当一个对象被借用后使用完毕，需要将其归还到对象池中。testOnReturn 用于控制这种归还过程。
    // 如果设置为 true，则在对象被归还到对象池时会对对象进行测试，以确定该对象是否可以继续被使用
    // 如果设置为 false，则不会进行测试
    // 通过设置 testOnReturn 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 testOnReturn 设置为 true，以便及时回收不再使用的对象；而在低并发场景下，为了提高系统吞吐量，可以将 testOnReturn 设置为 false，减少回收操作的次数和时间。
    private volatile boolean testOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;


    // 在空闲时是否对对象池中的对象进行测试. 默认值 false
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。testWhileIdle 用于控制这种回收过程。
    // 如果设置为 true，则在空闲时会对对象池中的对象进行测试，以确定哪些对象可以被回收
    // 如果设置为 false，则不会进行测试。
    // 通过设置 testWhileIdle 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 testWhileIdle 设置为 true，以便及时回收不再使用的对象；
    // 在低并发场景下，为了提高系统吞吐量，可以将 testWhileIdle 设置为 false，减少回收操作的次数和时间。
    private volatile boolean testWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;


    // 表示两次回收操作之间的时间间隔。 默认值-1
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间durationBetweenEvictionRuns 用于控制这种回收过程。
    // durationBetweenEvictionRuns 的值为一个 Duration 类型的对象，表示两次回收操作之间的时间间隔。在进行回收操作后，会等待指定的时间间隔后再次进行回收操作。
    // 通过设置 durationBetweenEvictionRuns 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 durationBetweenEvictionRuns 设置为较短的时间，减少回收操作的次数和时间。
    // 在低并发场景下，为了提高系统吞吐量，可以将 durationBetweenEvictionRuns 设置为较长的时间，增加回收操作的次数和时间，以提高资源利用率。
    private volatile Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS;

    // 表示每次回收空闲对象时需要进行的测试次数。 默认值3
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。numTestsPerEvictionRun 用于控制这种回收过程。
    // numTestsPerEvictionRun 的值为一个整数，表示每次回收空闲对象时需要进行的测试次数。在进行回收操作时，会从对象池中选择若干个空闲对象进行测试，
    // 以确定哪些对象可以被回收。测试的方式可以是随机选择、轮询等。
    // 通过设置 numTestsPerEvictionRun 的值，可以根据实际需求来选择合适的线程处理方式。
    // 在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，可以将 numTestsPerEvictionRun 设置为较小的值，减少回收操作的次数和时间。而在低并发场景下，为了提高系统吞吐量，
    // 可以将 numTestsPerEvictionRun 设置为较大的值，增加回收操作的次数和时间，以提高资源利用率。
    private volatile int numTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;


    // 表示对象池中空闲对象的最小可回收时长.默认值30分钟
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。minEvictableIdleDuration 用于控制这种回收过程。
    // minEvictableIdleDuration 的值为一个 Duration 类型的对象，表示空闲对象的最小可回收时长。当一个空闲对象在对象池中保持空闲状态的时间超过了这个时长，那么这个对象就可以被回收。
    // 通过设置 minEvictableIdleDuration 的值，可以根据实际需求来选择合适的线程处理方式。在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 minEvictableIdleDuration 设置为一个较短的时间，让空闲对象尽快被回收。
    // 而在低并发场景下，为了提高系统吞吐量，可以将 minEvictableIdleDuration 设置为一个较长的时间，让空闲对象在对象池中保持较长时间的空闲状态。
    private volatile Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;

    // 表示对象池中空闲对象的软最小可回收时长。 默认值-1
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间,softMinEvictableIdleDuration 用于控制这种回收过程
    // softMinEvictableIdleDuration 的值为一个 Duration 类型的对象，表示空闲对象的软最小可回收时长。当一个空闲对象在对象池中保持空闲状态的时间超过了这个时长，那么这个对象就可以被回收。
    // 通过设置 softMinEvictableIdleDuration 的值，可以根据实际需求来选择合适的线程处理方式。在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 softMinEvictableIdleDuration 设置为一个较短的时间，
    // 让空闲对象尽快被回收。而在低并发场景下，为了提高系统吞吐量，可以将 softMinEvictableIdleDuration 设置为一个较长的时间，让空闲对象在对象池中保持较长时间的空闲状态。
    private volatile Duration softMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION;

    // 当对象池中的对象数量超过最大值时，如何选择合适的对象进行回收
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。EvictionPolicy 用于控制这种回收过程。
    private volatile EvictionPolicy<T> evictionPolicy;

    // 表示当对象池关闭时，清理者线程（Evictor）的超时时间。 默认值10秒
    // 清理者线程负责在对象池关闭时，对对象池中的空闲对象进行清理操作，例如释放资源、关闭连接等。
    // evictorShutdownTimeoutDuration 用于设置清理者线程的超时时间，即在对象池关闭后，清理者线程执行清理操作的最长时间。
    // 为一个 Duration 类型的对象，表示等待的最长时间。当对象池关闭后，清理者线程会开始执行清理操作。如果在指定的超时时间内，清理者线程没有完成清理操作，那么对象池将抛出异常，表示无法正常关闭。
    // 通过设置 evictorShutdownTimeoutDuration 的值，可以根据实际需求来选择合适的线程处理方式。在高并发场景下，为了避免过多的线程长时间阻塞在等待队列上，
    // 可以将 evictorShutdownTimeoutDuration 设置为一个较短的时间，让清理者线程尽快完成清理操作。
    // 而在低并发场景下，为了提高系统吞吐量，可以将 evictorShutdownTimeoutDuration 设置为一个较长的时间，让清理者线程有足够的时间来完成清理操作。
    private volatile Duration evictorShutdownTimeoutDuration = BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT;


    // Internal (primarily state) attributes

    // 在 BaseGenericObjectPool 类中定义了一个名为 closeLock 的私有成员变量，它是一个 Object 类型的对象。
    // 这个变量通常用于实现同步机制，以确保在多线程环境下对资源的安全访问和操作。
    // closeLock 可能被用作一个锁对象，用于保护与关闭或释放资源相关的代码段。当多个线程尝试同时关闭或释放资源时，可以使用这个锁来确保只有一个线程能够执行关闭或释放操作，从而避免潜在的竞争条件和数据不一致问题。
    // 通过将 closeLock 声明为 final，可以确保它只能在构造函数中进行初始化，并且在整个类的生命周期内保持不变。这有助于提高代码的可读性和可维护性，并确保 closeLock 的正确使用。
    final Object closeLock = new Object();


    // 表示对象池是否已经关闭
    // 在 BaseGenericObjectPool 中，当对象池不再需要使用时，可以调用 close() 方法来关闭对象池。当对象池被关闭后，不能再向其中添加新的对象，也不能再从中借用或归还对象。此时，closed 变量的值会被设置为 true。
    // 需要注意的是，一旦对象池被关闭，就不能再重新打开或恢复其功能。因此，在使用对象池时，应该谨慎地控制其生命周期，并在适当的时候调用 close() 方法来释放资源。
    volatile boolean closed;


    /**
     * 驱逐策略的同步锁
     * evictionLock的作用是确保在执行驱逐操作时，池中的空闲对象列表（idleObjects）的访问是线程安全的。
     * 当对象池需要根据配置的驱逐策略移除一些空闲对象以节省资源时，会使用这个锁来同步相关的操作。
     */
    final Object evictionLock = new Object();

    /**
     * 用于执行驱逐策略
     * 对象池达到其配置的最大容量时处理额外的对象请求。当对象池中没有可用的对象来满足新的请求时，驱逐策略决定哪些对象应该从池中移除以腾出空间给新的对象。这通常涉及到移除那些被认为是不再需要或者最久未被使用的对象。
     * evictor组件负责实现这个策略。它根据配置的参数和规则来确定何时以及如何从池中移除对象。例如，如果一个对象长时间没有被借用，evictor可能会将其从池中移除。
     * 总结来说，evictor的作用是确保对象池能够有效地管理其内部的对象集合，通过移除不再需要的对象来避免资源的浪费，同时确保池中总是有足够的空闲对象来满足新的请求。
     */
    private Evictor evictor; // @GuardedBy("evictionLock")



    EvictionIterator evictionIterator; // @GuardedBy("evictionLock")

    /**
     * Class loader for evictor thread to use since, in a JavaEE or similar
     * environment, the context class loader for the evictor thread may not have
     * visibility of the correct factory. See POOL-161. Uses a weak reference to
     * avoid potential memory leaks if the Pool is discarded rather than closed.
     */
    private final WeakReference<ClassLoader> factoryClassLoader;
    // Monitoring (primarily JMX) attributes
    private final ObjectName objectName;
    private final String creationStackTrace;












    // 当前已经从对象池中借用出去的对象数量。
    // 当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。此时，会将借用出去的对象数量加一，并将该对象标记为已借用状态。
    // 通过维护 borrowedCount 变量的值，可以实时了解当前已经借用出去的对象数量。这对于监控和管理对象池的状态非常重要，可以帮助开发人员及时发现和解决潜在的问题，例如对象池中的资源被过度使用或滥用等。
    // borrowedCount 只是一个内部计数器，它并不直接暴露给外部使用。在使用 BaseGenericObjectPool 时，可以通过调用相应的方法来获取对象池的可用对象数量、已借用对象数量等信息，而不需要直接操作 borrowedCount 变量。
    private final AtomicLong borrowedCount = new AtomicLong();


    private final AtomicLong returnedCount = new AtomicLong();

    /**
     * 已经被创建的对象数量
     * 对象池中需要一个新的对象时，如果当前没有可用的对象，那么就会创建一个新的对象，并将 createdCount 的值递增，以便跟踪已创建的对象数量
     */
    final AtomicLong createdCount = new AtomicLong();

    /**
     * 已经被销毁的对象数量
     * 当对象池中的某个对象被销毁时，destroyedCount 的值会递增，以便跟踪已销毁的对象数量。
     */
    final AtomicLong destroyedCount = new AtomicLong();


    final AtomicLong destroyedByEvictorCount = new AtomicLong();
    final AtomicLong destroyedByBorrowValidationCount = new AtomicLong();

    private final StatsStore activeTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);
    private final StatsStore idleTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);
    private final StatsStore waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);











    // 线程安全的、原子引用（AtomicReference），它用于存储 maxBorrowWaitDuration 的值。
    // maxBorrowWaitDuration 表示在对象池中没有可用对象时，客户端等待获取对象的最大时间。
    // 当有多个线程同时尝试从对象池中借用对象时，这个值可以防止过多的线程长时间阻塞在等待队列上

    // maxBorrowWaitDuration 的值为一个 Duration 类型的对象，表示等待的最长时间。
    // 当一个线程尝试从对象池中借用对象时，如果对象池为空，线程会进入等待队列。maxBorrowWaitDuration 用于限制线程在等待队列中的等待时间。
    // 当等待时间超过 maxBorrowWaitDuration 时，线程将抛出异常，表示无法从对象池中获取到对象。
    private final AtomicReference<Duration> maxBorrowWaitDuration = new AtomicReference<>(Duration.ZERO);


    private volatile SwallowedExceptionListener swallowedExceptionListener;
    private volatile boolean messageStatistics;

    /**
     * 废弃对象跟踪的其他配置属性。
     */
    protected volatile AbandonedConfig abandonedConfig;


    /**
     * 处理JMX注册（如果需要）和监控所需的初始化.
     *
     * @param config        池配置
     * @param jmxNameBase   新池的默认基本JMX名称，除非被配置覆盖
     * @param jmxNamePrefix Prefix to be used for JMX name for the new pool
     */
    public BaseGenericObjectPool(final BaseObjectPoolConfig<T> config,
                                 final String jmxNameBase, final String jmxNamePrefix) {

        // 是否注册 JMX,默认值 DEFAULT_JMX_ENABLE = true
        if (config.getJmxEnabled()) {
            this.objectName = jmxRegister(config, jmxNameBase, jmxNamePrefix);
        } else {
            this.objectName = null;
        }

        // Populate the creation stack trace
        this.creationStackTrace = getStackTrace(new Exception());

        // save the current TCCL (if any) to be used later by the evictor Thread
        final ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            factoryClassLoader = null;
        } else {
            factoryClassLoader = new WeakReference<>(cl);
        }

        fairness = config.getFairness();
    }


    /**
     * Appends statistics if enabled.
     * <p>
     * Statistics may not accurately reflect snapshot state at the time of the exception because we do not want to lock the pool when gathering this
     * information.
     * </p>
     *
     * @param string The root string.
     * @return The root string plus statistics.
     */
    String appendStats(final String string) {
        return messageStatistics ? string + ", " + getStatsString() : string;
    }


    /**
     * 验证池是否已打开
     *
     * @throws IllegalStateException if the pool is closed.
     */
    final void assertOpen() throws IllegalStateException {
        if (isClosed()) {
            throw new IllegalStateException("Pool not open");
        }
    }


    /**
     * 关闭池，销毁剩余的空闲对象，如果在JMX中注册，则取消注册。
     */
    @Override
    public abstract void close();


    /**
     * 根据池对象的状态创建要删除的池对象列表。
     *
     * @param abandonedConfig The abandoned configuration.
     * @param allObjects      PooledObject instances to consider.
     * @return 基于状态要删除的池对象的列表。
     */
    ArrayList<PooledObject<T>> createRemoveList(final AbandonedConfig abandonedConfig,
                                                final Map<IdentityWrapper<T>, PooledObject<T>> allObjects) {

        // 当一个连接在一段时间内没有任何活动时，它被认为是废弃的  默认时长: 5分钟
        final Instant timeout = Instant.now().minus(abandonedConfig.getRemoveAbandonedTimeoutDuration());

        // 初始化一个list，用来存储需要删除的对象池中的废弃的对象
        final ArrayList<PooledObject<T>> remove = new ArrayList<>();

        allObjects.values().forEach(pooledObject -> {
            // 锁定该对象
            synchronized (pooledObject) {
                if (
                    // 正在使用状态
                    // 1、表示对象池中的对象已经被分配给某个线程使用
                    // 2、在对象池中，分配状态表示该对象已经被分配给某个线程使用，并且该线程正在使用该对象。
                    // 3、当该线程完成对对象的使用后，可以将对象归还到对象池中，以便其他线程可以使用。
                        pooledObject.getState() == PooledObjectState.ALLOCATED

                                // pooledObject.getLastUsedTime()返回的就是对象的lastUseTime属性值，见名知意，就是该对象上一次被使用的时间
                                // 这个值我们在解析borrowObject时提到过一点，就是在调用对象的allocte方法时，会把当前的毫秒时间戳赋值给lastUseTime
                                // 所以如果：lastUseTime < timeout ,说明该对象已经在（removeAbandonedTimeout -> 默认300秒）的时间里没有被再次使用了。
                                && pooledObject.getLastUsedInstant().compareTo(timeout) <= 0
                ) {
                    // 即将无效
                    // 1、表示对象池中的对象被放弃
                    // 2、在对象池中，放弃状态通常用于表示该对象已经被标记为不可用或已被移除，并且没有被其他线程重新分配给其他任务。
                    // 3、如果对象处于放弃状态，那么它将无法被线程使用，并且可能被其他线程重新分配给其他任务。
                    pooledObject.markAbandoned();
                    remove.add(pooledObject);
                }
            }
        });
        return remove;
    }

    /**
     * Tries to ensure that the configured minimum number of idle instances are
     * available in the pool.
     *
     * @throws Exception if an error occurs creating idle instances
     */
    abstract void ensureMinIdle() throws Exception;

    /**
     * Perform {@code numTests} idle object eviction tests, evicting
     * examined objects that meet the criteria for eviction. If
     * {@code testWhileIdle} is true, examined objects are validated
     * when visited (and removed if invalid); otherwise only objects that
     * have been idle for more than {@code minEvicableIdleTimeMillis}
     * are removed.
     *
     * @throws Exception when there is a problem evicting idle objects.
     */
    public abstract void evict() throws Exception;

    /**
     * Gets whether to block when the {@code borrowObject()} method is
     * invoked when the pool is exhausted (the maximum number of "active"
     * objects has been reached).
     *
     * @return {@code true} if {@code borrowObject()} should block
     * when the pool is exhausted
     * @see #setBlockWhenExhausted
     */
    public final boolean getBlockWhenExhausted() {
        return blockWhenExhausted;
    }

    /**
     * Gets the total number of objects successfully borrowed from this pool over the
     * lifetime of the pool.
     *
     * @return the borrowed object count
     */
    public final long getBorrowedCount() {
        return borrowedCount.get();
    }

    /**
     * Gets the total number of objects created for this pool over the lifetime of
     * the pool.
     *
     * @return the created object count
     */
    public final long getCreatedCount() {
        return createdCount.get();
    }

    /**
     * Gets the stack trace for the call that created this pool. JMX
     * registration may trigger a memory leak so it is important that pools are
     * deregistered when no longer used by calling the {@link #close()} method.
     * This method is provided to assist with identifying code that creates but
     * does not close it thereby creating a memory leak.
     *
     * @return pool creation stack trace
     */
    public final String getCreationStackTrace() {
        return creationStackTrace;
    }

    /**
     * Gets the total number of objects destroyed by this pool as a result of failing
     * validation during {@code borrowObject()} over the lifetime of the
     * pool.
     *
     * @return validation destroyed object count
     */
    public final long getDestroyedByBorrowValidationCount() {
        return destroyedByBorrowValidationCount.get();
    }

    /**
     * Gets the total number of objects destroyed by the evictor associated with this
     * pool over the lifetime of the pool.
     *
     * @return the evictor destroyed object count
     */
    public final long getDestroyedByEvictorCount() {
        return destroyedByEvictorCount.get();
    }

    /**
     * Gets the total number of objects destroyed by this pool over the lifetime of
     * the pool.
     *
     * @return the destroyed object count
     */
    public final long getDestroyedCount() {
        return destroyedCount.get();
    }

    /**
     * Gets the duration to sleep between runs of the idle
     * object evictor thread. When non-positive, no idle object evictor thread
     * will be run.
     *
     * @return number of milliseconds to sleep between evictor runs
     * @see #setTimeBetweenEvictionRuns
     * @since 2.11.0
     */
    public final Duration getDurationBetweenEvictionRuns() {
        return durationBetweenEvictionRuns;
    }

    /**
     * Gets the {@link EvictionPolicy} defined for this pool.
     *
     * @return the eviction policy
     * @since 2.4
     * @since 2.6.0 Changed access from protected to public.
     */
    public EvictionPolicy<T> getEvictionPolicy() {
        return evictionPolicy;
    }

    /**
     * Gets the name of the {@link EvictionPolicy} implementation that is
     * used by this pool.
     *
     * @return The fully qualified class name of the {@link EvictionPolicy}
     * @see #setEvictionPolicyClassName(String)
     */
    public final String getEvictionPolicyClassName() {
        return evictionPolicy.getClass().getName();
    }

    /**
     * Gets the timeout that will be used when waiting for the Evictor to
     * shutdown if this pool is closed and it is the only pool still using the
     * the value for the Evictor.
     *
     * @return The timeout that will be used while waiting for
     * the Evictor to shut down.
     * @since 2.10.0
     * @deprecated Use {@link #getEvictorShutdownTimeoutDuration()}.
     */
    @Deprecated
    public final Duration getEvictorShutdownTimeout() {
        return evictorShutdownTimeoutDuration;
    }

    /**
     * Gets the timeout that will be used when waiting for the Evictor to
     * shutdown if this pool is closed and it is the only pool still using the
     * the value for the Evictor.
     *
     * @return The timeout that will be used while waiting for
     * the Evictor to shut down.
     * @since 2.11.0
     */
    public final Duration getEvictorShutdownTimeoutDuration() {
        return evictorShutdownTimeoutDuration;
    }

    /**
     * Gets the timeout that will be used when waiting for the Evictor to
     * shutdown if this pool is closed and it is the only pool still using the
     * the value for the Evictor.
     *
     * @return The timeout in milliseconds that will be used while waiting for
     * the Evictor to shut down.
     * @deprecated Use {@link #getEvictorShutdownTimeoutDuration()}.
     */
    @Deprecated
    public final long getEvictorShutdownTimeoutMillis() {
        return evictorShutdownTimeoutDuration.toMillis();
    }

    /**
     * Gets whether or not the pool serves threads waiting to borrow objects fairly.
     * True means that waiting threads are served as if waiting in a FIFO queue.
     *
     * @return {@code true} if waiting threads are to be served
     * by the pool in arrival order
     */
    public final boolean getFairness() {
        return fairness;
    }

    /**
     * Gets the name under which the pool has been registered with the
     * platform MBean server or {@code null} if the pool has not been
     * registered.
     *
     * @return the JMX name
     */
    public final ObjectName getJmxName() {
        return objectName;
    }

    /**
     * Gets whether the pool has LIFO (last in, first out) behavior with
     * respect to idle objects - always returning the most recently used object
     * from the pool, or as a FIFO (first in, first out) queue, where the pool
     * always returns the oldest object in the idle object pool.
     *
     * @return {@code true} if the pool is configured with LIFO behavior
     * or {@code false} if the pool is configured with FIFO
     * behavior
     * @see #setLifo
     */
    public final boolean getLifo() {
        return lifo;
    }

    /**
     * Gets whether this pool identifies and logs any abandoned objects.
     *
     * @return {@code true} if abandoned object removal is configured for this
     * pool and removal events are to be logged otherwise {@code false}
     * @see AbandonedConfig#getLogAbandoned()
     * @since 2.11.0
     */
    public boolean getLogAbandoned() {
        final AbandonedConfig ac = this.abandonedConfig;
        return ac != null && ac.getLogAbandoned();
    }

    /**
     * Gets the maximum time a thread has waited to borrow objects from the pool.
     *
     * @return maximum wait time in milliseconds since the pool was created
     * @since 2.12.0
     */
    public final Duration getMaxBorrowWaitDuration() {
        return maxBorrowWaitDuration.get();
    }

    /**
     * Gets the maximum time a thread has waited to borrow objects from the pool.
     *
     * @return maximum wait time in milliseconds since the pool was created
     * @deprecated Use {@link #getMaxBorrowWaitDuration()}.
     */
    @Deprecated
    public final long getMaxBorrowWaitTimeMillis() {
        return maxBorrowWaitDuration.get().toMillis();
    }

    /**
     * Gets the maximum number of objects that can be allocated by the pool
     * (checked out to clients, or idle awaiting checkout) at a given time. When
     * negative, there is no limit to the number of objects that can be
     * managed by the pool at one time.
     *
     * @return the cap on the total number of object instances managed by the
     * pool.
     * @see #setMaxTotal
     */
    public final int getMaxTotal() {
        return maxTotal;
    }

    /**
     * Gets the maximum duration the
     * {@code borrowObject()} method should block before throwing an
     * exception when the pool is exhausted and
     * {@link #getBlockWhenExhausted} is true. When less than 0, the
     * {@code borrowObject()} method may block indefinitely.
     *
     * @return the maximum number of milliseconds {@code borrowObject()}
     * will block.
     * @see #setMaxWait
     * @see #setBlockWhenExhausted
     * @since 2.11.0
     */
    public final Duration getMaxWaitDuration() {
        return maxWaitDuration;
    }

    /**
     * Gets the maximum amount of time (in milliseconds) the
     * {@code borrowObject()} method should block before throwing an
     * exception when the pool is exhausted and
     * {@link #getBlockWhenExhausted} is true. When less than 0, the
     * {@code borrowObject()} method may block indefinitely.
     *
     * @return the maximum number of milliseconds {@code borrowObject()}
     * will block.
     * @see #setMaxWait
     * @see #setBlockWhenExhausted
     * @deprecated Use {@link #getMaxWaitDuration()}.
     */
    @Deprecated
    public final long getMaxWaitMillis() {
        return maxWaitDuration.toMillis();
    }

    /**
     * Gets the mean time objects are active for based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool.
     *
     * @return mean time an object has been checked out from the pool among
     * recently returned objects.
     * @since 2.12.0
     */
    public final Duration getMeanActiveDuration() {
        return activeTimes.getMeanDuration();
    }

    /**
     * Gets the mean time objects are active for based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool.
     *
     * @return mean time an object has been checked out from the pool among
     * recently returned objects.
     * @deprecated Use {@link #getMeanActiveDuration()}.
     */
    @Deprecated
    public final long getMeanActiveTimeMillis() {
        return activeTimes.getMean();
    }

    /**
     * Gets the mean time threads wait to borrow an object based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool.
     *
     * @return mean time in milliseconds that a recently served thread has had
     * to wait to borrow an object from the pool.
     * @since 2.12.0
     */
    public final Duration getMeanBorrowWaitDuration() {
        return waitTimes.getMeanDuration();
    }

    /**
     * Gets the mean time threads wait to borrow an object based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool.
     *
     * @return mean time in milliseconds that a recently served thread has had
     * to wait to borrow an object from the pool.
     * @deprecated Use {@link #getMeanBorrowWaitDuration()}.
     */
    @Deprecated
    public final long getMeanBorrowWaitTimeMillis() {
        return waitTimes.getMean();
    }

    /**
     * Gets the mean time objects are idle for based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool.
     *
     * @return mean time an object has been idle in the pool among recently
     * borrowed objects.
     * @since 2.12.0
     */
    public final Duration getMeanIdleDuration() {
        return idleTimes.getMeanDuration();
    }

    /**
     * Gets the mean time objects are idle for based on the last {@link
     * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool.
     *
     * @return mean time an object has been idle in the pool among recently
     * borrowed objects.
     * @deprecated Use {@link #getMeanIdleDuration()}.
     */
    @Deprecated
    public final long getMeanIdleTimeMillis() {
        return idleTimes.getMean();
    }

    /**
     * Gets whether to include statistics in exception messages.
     * <p>
     * Statistics may not accurately reflect snapshot state at the time of the exception because we do not want to lock the pool when gathering this
     * information.
     * </p>
     *
     * @return whether to include statistics in exception messages.
     * @since 2.11.0
     */
    public boolean getMessageStatistics() {
        return messageStatistics;
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction
     * @see #setMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     * @since 2.11.0
     */
    public final Duration getMinEvictableIdleDuration() {
        return minEvictableIdleDuration;
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction
     * @see #setMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     * @since 2.10.0
     * @deprecated Use {@link #getMinEvictableIdleDuration()}.
     */
    @Deprecated
    public final Duration getMinEvictableIdleTime() {
        return minEvictableIdleDuration;
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction
     * @see #setMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     * @deprecated Use {@link #getMinEvictableIdleDuration()}.
     */
    @Deprecated
    public final long getMinEvictableIdleTimeMillis() {
        return minEvictableIdleDuration.toMillis();
    }

    /**
     * Gets the number of instances currently idle in this pool.
     *
     * @return count of instances available for checkout from the pool
     */
    public abstract int getNumIdle();

    /**
     * Gets the maximum number of objects to examine during each run (if any)
     * of the idle object evictor thread. When positive, the number of tests
     * performed for a run will be the minimum of the configured value and the
     * number of idle instances in the pool. When negative, the number of tests
     * performed will be <code>ceil({@link #getNumIdle}/
     * abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the
     * value is {@code -n} roughly one nth of the idle objects will be
     * tested per run.
     *
     * @return max number of objects to examine during each evictor run
     * @see #setNumTestsPerEvictionRun
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public final int getNumTestsPerEvictionRun() {
        return numTestsPerEvictionRun;
    }

    /**
     * Gets whether a check is made for abandoned objects when an object is borrowed
     * from this pool.
     *
     * @return {@code true} if abandoned object removal is configured to be
     * activated by borrowObject otherwise {@code false}
     * @see AbandonedConfig#getRemoveAbandonedOnBorrow()
     * @since 2.11.0
     */
    public boolean getRemoveAbandonedOnBorrow() {
        final AbandonedConfig ac = this.abandonedConfig;
        return ac != null && ac.getRemoveAbandonedOnBorrow();
    }

    /**
     * Gets whether a check is made for abandoned objects when the evictor runs.
     *
     * @return {@code true} if abandoned object removal is configured to be
     * activated when the evictor runs otherwise {@code false}
     * @see AbandonedConfig#getRemoveAbandonedOnMaintenance()
     * @since 2.11.0
     */
    public boolean getRemoveAbandonedOnMaintenance() {
        final AbandonedConfig ac = this.abandonedConfig;
        return ac != null && ac.getRemoveAbandonedOnMaintenance();
    }

    /**
     * Gets the timeout before which an object will be considered to be
     * abandoned by this pool.
     *
     * @return The abandoned object timeout in seconds if abandoned object
     * removal is configured for this pool; Integer.MAX_VALUE otherwise.
     * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration()
     * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration()
     * @since 2.11.0
     * @deprecated Use {@link #getRemoveAbandonedTimeoutDuration()}.
     */
    @Deprecated
    public int getRemoveAbandonedTimeout() {
        return (int) getRemoveAbandonedTimeoutDuration().getSeconds();
    }

    /**
     * Gets the timeout before which an object will be considered to be
     * abandoned by this pool.
     *
     * @return The abandoned object timeout in seconds if abandoned object
     * removal is configured for this pool; Integer.MAX_VALUE otherwise.
     * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration()
     * @since 2.11.0
     */
    public Duration getRemoveAbandonedTimeoutDuration() {
        final AbandonedConfig ac = this.abandonedConfig;
        return ac != null ? ac.getRemoveAbandonedTimeoutDuration() : DEFAULT_REMOVE_ABANDONED_TIMEOUT;
    }

    /**
     * Gets the total number of objects returned to this pool over the lifetime of
     * the pool. This excludes attempts to return the same object multiple
     * times.
     *
     * @return the returned object count
     */
    public final long getReturnedCount() {
        return returnedCount.get();
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTime} (that is, if
     * {@link #getMinEvictableIdleTime} is positive, then
     * {@link #getSoftMinEvictableIdleTime} is ignored).
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction if minIdle instances are available
     * @see #setSoftMinEvictableIdleDuration(Duration)
     * @since 2.11.0
     */
    public final Duration getSoftMinEvictableIdleDuration() {
        return softMinEvictableIdleDuration;
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTime} (that is, if
     * {@link #getMinEvictableIdleTime} is positive, then
     * {@link #getSoftMinEvictableIdleTime} is ignored).
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction if minIdle instances are available
     * @see #setSoftMinEvictableIdleDuration(Duration)
     * @since 2.10.0
     * @deprecated Use {@link #getSoftMinEvictableIdleDuration}.
     */
    @Deprecated
    public final Duration getSoftMinEvictableIdleTime() {
        return softMinEvictableIdleDuration;
    }

    /**
     * Gets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setTimeBetweenEvictionRunsMillis(long)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTimeMillis} (that is, if
     * {@link #getMinEvictableIdleTimeMillis} is positive, then
     * {@link #getSoftMinEvictableIdleTimeMillis} is ignored).
     *
     * @return minimum amount of time an object may sit idle in the pool before
     * it is eligible for eviction if minIdle instances are available
     * @see #setSoftMinEvictableIdleTimeMillis
     * @deprecated Use {@link #getSoftMinEvictableIdleTime()}.
     */
    @Deprecated
    public final long getSoftMinEvictableIdleTimeMillis() {
        return softMinEvictableIdleDuration.toMillis();
    }

    /**
     * Gets the stack trace of an exception as a string.
     *
     * @param e exception to trace
     * @return exception stack trace as a string
     */
    private String getStackTrace(final Exception e) {
        // Need the exception in string form to prevent the retention of
        // references to classes in the stack trace that could trigger a memory
        // leak in a container environment.
        final Writer w = new StringWriter();
        final PrintWriter pw = new PrintWriter(w);
        e.printStackTrace(pw);
        return w.toString();
    }

    /**
     * Gets a statistics string.
     *
     * @return a statistics string.
     */
    String getStatsString() {
        // Simply listed in AB order.
        return String.format(
                "activeTimes=%s, blockWhenExhausted=%s, borrowedCount=%,d, closed=%s, createdCount=%,d, destroyedByBorrowValidationCount=%,d, " +
                        "destroyedByEvictorCount=%,d, evictorShutdownTimeoutDuration=%s, fairness=%s, idleTimes=%s, lifo=%s, maxBorrowWaitDuration=%s, " +
                        "maxTotal=%s, maxWaitDuration=%s, minEvictableIdleDuration=%s, numTestsPerEvictionRun=%s, returnedCount=%s, " +
                        "softMinEvictableIdleDuration=%s, testOnBorrow=%s, testOnCreate=%s, testOnReturn=%s, testWhileIdle=%s, " +
                        "durationBetweenEvictionRuns=%s, waitTimes=%s",
                activeTimes.getValues(), blockWhenExhausted, borrowedCount.get(), closed, createdCount.get(), destroyedByBorrowValidationCount.get(),
                destroyedByEvictorCount.get(), evictorShutdownTimeoutDuration, fairness, idleTimes.getValues(), lifo, maxBorrowWaitDuration.get(),
                maxTotal, maxWaitDuration, minEvictableIdleDuration, numTestsPerEvictionRun, returnedCount, softMinEvictableIdleDuration, testOnBorrow,
                testOnCreate, testOnReturn, testWhileIdle, durationBetweenEvictionRuns, waitTimes.getValues());
    }

    /**
     * Gets the listener used (if any) to receive notifications of exceptions
     * unavoidably swallowed by the pool.
     *
     * @return The listener or {@code null} for no listener
     */
    public final SwallowedExceptionListener getSwallowedExceptionListener() {
        return swallowedExceptionListener;
    }

    /**
     * Gets whether objects borrowed from the pool will be validated before
     * being returned from the {@code borrowObject()} method. Validation is
     * performed by the {@code validateObject()} method of the factory
     * associated with the pool. If the object fails to validate, it will be
     * removed from the pool and destroyed, and a new attempt will be made to
     * borrow an object from the pool.
     *
     * @return {@code true} if objects are validated before being returned
     * from the {@code borrowObject()} method
     * @see #setTestOnBorrow
     */
    public final boolean getTestOnBorrow() {
        return testOnBorrow;
    }

    /**
     * Gets whether objects created for the pool will be validated before
     * being returned from the {@code borrowObject()} method. Validation is
     * performed by the {@code validateObject()} method of the factory
     * associated with the pool. If the object fails to validate, then
     * {@code borrowObject()} will fail.
     *
     * @return {@code true} if newly created objects are validated before
     * being returned from the {@code borrowObject()} method
     * @see #setTestOnCreate
     * @since 2.2
     */
    public final boolean getTestOnCreate() {
        return testOnCreate;
    }

    /**
     * Gets whether objects borrowed from the pool will be validated when
     * they are returned to the pool via the {@code returnObject()} method.
     * Validation is performed by the {@code validateObject()} method of
     * the factory associated with the pool. Returning objects that fail validation
     * are destroyed rather then being returned the pool.
     *
     * @return {@code true} if objects are validated on return to
     * the pool via the {@code returnObject()} method
     * @see #setTestOnReturn
     */
    public final boolean getTestOnReturn() {
        return testOnReturn;
    }

    /**
     * Gets whether objects sitting idle in the pool will be validated by the
     * idle object evictor (if any - see
     * {@link #setDurationBetweenEvictionRuns(Duration)}). Validation is performed
     * by the {@code validateObject()} method of the factory associated
     * with the pool. If the object fails to validate, it will be removed from
     * the pool and destroyed.
     *
     * @return {@code true} if objects will be validated by the evictor
     * @see #setTestWhileIdle
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public final boolean getTestWhileIdle() {
        return testWhileIdle;
    }

    /**
     * Gets the duration to sleep between runs of the idle
     * object evictor thread. When non-positive, no idle object evictor thread
     * will be run.
     *
     * @return number of milliseconds to sleep between evictor runs
     * @see #setTimeBetweenEvictionRuns
     * @since 2.10.0
     * @deprecated {@link #getDurationBetweenEvictionRuns()}.
     */
    @Deprecated
    public final Duration getTimeBetweenEvictionRuns() {
        return durationBetweenEvictionRuns;
    }

    /**
     * Gets the number of milliseconds to sleep between runs of the idle
     * object evictor thread. When non-positive, no idle object evictor thread
     * will be run.
     *
     * @return number of milliseconds to sleep between evictor runs
     * @see #setTimeBetweenEvictionRunsMillis
     * @deprecated Use {@link #getDurationBetweenEvictionRuns()}.
     */
    @Deprecated
    public final long getTimeBetweenEvictionRunsMillis() {
        return durationBetweenEvictionRuns.toMillis();
    }

    /**
     * Tests whether or not abandoned object removal is configured for this pool.
     *
     * @return true if this pool is configured to detect and remove
     * abandoned objects
     * @since 2.11.0
     */
    public boolean isAbandonedConfig() {
        return abandonedConfig != null;
    }


    public final boolean isClosed() {
        return closed;
    }

    /**
     * Registers the pool with the platform MBean server.
     * The registered name will be
     * {@code jmxNameBase + jmxNamePrefix + i} where i is the least
     * integer greater than or equal to 1 such that the name is not already
     * registered. Swallows MBeanRegistrationException, NotCompliantMBeanException
     * returning null.
     *
     * @param config        Pool configuration
     * @param jmxNameBase   default base JMX name for this pool
     * @param jmxNamePrefix name prefix
     * @return registered ObjectName, null if registration fails
     */
    private ObjectName jmxRegister(final BaseObjectPoolConfig<T> config,
                                   final String jmxNameBase, String jmxNamePrefix) {
        ObjectName newObjectName = null;
        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        int i = 1;
        boolean registered = false;
        String base = config.getJmxNameBase();
        if (base == null) {
            base = jmxNameBase;
        }
        while (!registered) {
            try {
                ObjectName objName;
                // Skip the numeric suffix for the first pool in case there is
                // only one so the names are cleaner.
                if (i == 1) {
                    objName = new ObjectName(base + jmxNamePrefix);
                } else {
                    objName = new ObjectName(base + jmxNamePrefix + i);
                }
                if (!mbs.isRegistered(objName)) {
                    mbs.registerMBean(this, objName);
                    newObjectName = objName;
                    registered = true;
                } else {
                    // Increment the index and try again
                    i++;
                }
            } catch (final MalformedObjectNameException e) {
                if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals(
                        jmxNamePrefix) && jmxNameBase.equals(base)) {
                    // Shouldn't happen. Skip registration if it does.
                    registered = true;
                } else {
                    // Must be an invalid name. Use the defaults instead.
                    jmxNamePrefix =
                            BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX;
                    base = jmxNameBase;
                }
            } catch (final InstanceAlreadyExistsException e) {
                // Increment the index and try again
                i++;
            } catch (final MBeanRegistrationException | NotCompliantMBeanException e) {
                // Shouldn't happen. Skip registration if it does.
                registered = true;
            }
        }
        return newObjectName;
    }

    /**
     * Unregisters this pool's MBean.
     */
    final void jmxUnregister() {
        if (objectName != null) {
            try {
                ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
            } catch (final MBeanRegistrationException | InstanceNotFoundException e) {
                swallowException(e);
            }
        }
    }

    /**
     * 标记返回状态
     *
     * @param pooledObject 实例返回到键控池
     */
    protected void markReturningState(final PooledObject<T> pooledObject) {

        // 如果p不为空，这里加上了同步锁，保证内部逻辑处理的原子性。因为涉及到最重要的状态判断和变更。
        synchronized (pooledObject) {
            // 要归还的对象状态就一定应该是ALLOCATED，标识使用中，如果状态不是ALLOCATED，就说明状态不符合预期，抛出个异常
            if (pooledObject.getState() != PooledObjectState.ALLOCATED) {
                throw new IllegalStateException("Object has already been returned to this pool or is invalid");
            }
            // 如果状态符合预期
            // 调用PooledObject对象的markReturning方法进行对象状态变更
            // PooledObject的默认实现类DefaultPooledObject的该方法只有一行代码：
            // state = PooledObjectState.RETURNING; //就是改变一下状态。
            pooledObject.markReturning();
        }
    }

    /**
     * Sets the abandoned object removal configuration.
     *
     * @param abandonedConfig the new configuration to use. This is used by value.
     * @see AbandonedConfig
     * @since 2.11.0
     */
    public void setAbandonedConfig(final AbandonedConfig abandonedConfig) {
        this.abandonedConfig = AbandonedConfig.copy(abandonedConfig);
    }

    /**
     * Sets whether to block when the {@code borrowObject()} method is
     * invoked when the pool is exhausted (the maximum number of "active"
     * objects has been reached).
     *
     * @param blockWhenExhausted {@code true} if
     *                           {@code borrowObject()} should block
     *                           when the pool is exhausted
     * @see #getBlockWhenExhausted
     */
    public final void setBlockWhenExhausted(final boolean blockWhenExhausted) {
        this.blockWhenExhausted = blockWhenExhausted;
    }

    /**
     * Sets the receiver with the given configuration.
     *
     * @param config Initialization source.
     */
    protected void setConfig(final BaseObjectPoolConfig<T> config) {
        setLifo(config.getLifo());
        setMaxWait(config.getMaxWaitDuration());
        setBlockWhenExhausted(config.getBlockWhenExhausted());
        setTestOnCreate(config.getTestOnCreate());
        setTestOnBorrow(config.getTestOnBorrow());
        setTestOnReturn(config.getTestOnReturn());
        setTestWhileIdle(config.getTestWhileIdle());
        setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun());
        setMinEvictableIdleDuration(config.getMinEvictableIdleDuration());
        setDurationBetweenEvictionRuns(config.getDurationBetweenEvictionRuns());
        setSoftMinEvictableIdleDuration(config.getSoftMinEvictableIdleDuration());
        final EvictionPolicy<T> policy = config.getEvictionPolicy();
        if (policy == null) {
            // Use the class name (pre-2.6.0 compatible)
            setEvictionPolicyClassName(config.getEvictionPolicyClassName());
        } else {
            // Otherwise, use the class (2.6.0 feature)
            setEvictionPolicy(policy);
        }
        setEvictorShutdownTimeout(config.getEvictorShutdownTimeoutDuration());
    }

    /**
     * Sets the number of milliseconds to sleep between runs of the idle object evictor thread.
     * <ul>
     * <li>When positive, the idle object evictor thread starts.</li>
     * <li>When null or non-positive, no idle object evictor thread runs.</li>
     * </ul>
     *
     * @param timeBetweenEvictionRuns duration to sleep between evictor runs
     * @see #getDurationBetweenEvictionRuns()
     * @since 2.12.0
     */
    public final void setDurationBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) {
        this.durationBetweenEvictionRuns = PoolImplUtils.nonNull(timeBetweenEvictionRuns, BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS);
        startEvictor(this.durationBetweenEvictionRuns);
    }

    /**
     * Sets the eviction policy for this pool.
     *
     * @param evictionPolicy the eviction policy for this pool.
     * @since 2.6.0
     */
    public void setEvictionPolicy(final EvictionPolicy<T> evictionPolicy) {
        this.evictionPolicy = evictionPolicy;
    }

    /**
     * Sets the eviction policy.
     *
     * @param className   Eviction policy class name.
     * @param classLoader Load the class from this class loader.
     * @throws LinkageError                if the linkage fails
     * @throws ExceptionInInitializerError if the initialization provoked by this method fails
     * @throws ClassNotFoundException      if the class cannot be located by the specified class loader
     * @throws IllegalAccessException      if this {@code Constructor} object is enforcing Java language access control and the underlying constructor is
     *                                     inaccessible.
     * @throws IllegalArgumentException    if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if,
     *                                     after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion; if
     *                                     this constructor pertains to an enum type.
     * @throws InstantiationException      if the class that declares the underlying constructor represents an abstract class.
     * @throws InvocationTargetException   if the underlying constructor throws an exception.
     * @throws ExceptionInInitializerError if the initialization provoked by this method fails.
     * @throws NoSuchMethodException       if a matching method is not found.
     * @throws SecurityException           If a security manage is present and the caller's class loader is not the same as or an ancestor of the class loader for the
     *                                     current class and invocation of {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} denies access to the package of this class.
     */
    @SuppressWarnings("unchecked")
    private void setEvictionPolicy(final String className, final ClassLoader classLoader)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        final Class<?> clazz = Class.forName(className, true, classLoader);
        final Object policy = clazz.getConstructor().newInstance();
        this.evictionPolicy = (EvictionPolicy<T>) policy;
    }

    /**
     * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to
     * load the class using the thread context class loader. If that fails, the use the class loader for the
     * {@link EvictionPolicy} interface.
     *
     * @param evictionPolicyClassName the fully qualified class name of the new eviction policy
     * @see #getEvictionPolicyClassName()
     * @since 2.6.0 If loading the class using the thread context class loader fails, use the class loader for the
     * {@link EvictionPolicy} interface.
     */
    public final void setEvictionPolicyClassName(final String evictionPolicyClassName) {
        setEvictionPolicyClassName(evictionPolicyClassName, Thread.currentThread().getContextClassLoader());
    }

    /**
     * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to
     * load the class using the given class loader. If that fails, use the class loader for the {@link EvictionPolicy}
     * interface.
     *
     * @param evictionPolicyClassName the fully qualified class name of the new eviction policy
     * @param classLoader             the class loader to load the given {@code evictionPolicyClassName}.
     * @see #getEvictionPolicyClassName()
     * @since 2.6.0 If loading the class using the given class loader fails, use the class loader for the
     * {@link EvictionPolicy} interface.
     */
    public final void setEvictionPolicyClassName(final String evictionPolicyClassName, final ClassLoader classLoader) {
        // Getting epClass here and now best matches the caller's environment
        final Class<?> epClass = EvictionPolicy.class;
        final ClassLoader epClassLoader = epClass.getClassLoader();
        try {
            try {
                setEvictionPolicy(evictionPolicyClassName, classLoader);
            } catch (final ClassCastException | ClassNotFoundException e) {
                setEvictionPolicy(evictionPolicyClassName, epClassLoader);
            }
        } catch (final ClassCastException e) {
            throw new IllegalArgumentException("Class " + evictionPolicyClassName + " from class loaders [" +
                    classLoader + ", " + epClassLoader + "] do not implement " + EVICTION_POLICY_TYPE_NAME);
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException |
                       InvocationTargetException | NoSuchMethodException e) {
            throw new IllegalArgumentException(
                    "Unable to create " + EVICTION_POLICY_TYPE_NAME + " instance of type " + evictionPolicyClassName,
                    e);
        }
    }

    /**
     * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the
     * only pool still using the value for the Evictor.
     *
     * @param evictorShutdownTimeout the timeout in milliseconds that will be used while waiting for the Evictor
     *                               to shut down.
     * @since 2.10.0
     */
    public final void setEvictorShutdownTimeout(final Duration evictorShutdownTimeout) {
        this.evictorShutdownTimeoutDuration = PoolImplUtils.nonNull(evictorShutdownTimeout, BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT);
    }

    /**
     * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the
     * only pool still using the value for the Evictor.
     *
     * @param evictorShutdownTimeoutMillis the timeout in milliseconds that will be used while waiting for the Evictor
     *                                     to shut down.
     * @deprecated Use {@link #setEvictorShutdownTimeout(Duration)}.
     */
    @Deprecated
    public final void setEvictorShutdownTimeoutMillis(final long evictorShutdownTimeoutMillis) {
        setEvictorShutdownTimeout(Duration.ofMillis(evictorShutdownTimeoutMillis));
    }

    /**
     * Sets whether the pool has LIFO (last in, first out) behavior with
     * respect to idle objects - always returning the most recently used object
     * from the pool, or as a FIFO (first in, first out) queue, where the pool
     * always returns the oldest object in the idle object pool.
     *
     * @param lifo {@code true} if the pool is to be configured with LIFO
     *             behavior or {@code false} if the pool is to be
     *             configured with FIFO behavior
     * @see #getLifo()
     */
    public final void setLifo(final boolean lifo) {
        this.lifo = lifo;
    }

    /**
     * Sets the cap on the number of objects that can be allocated by the pool
     * (checked out to clients, or idle awaiting checkout) at a given time. Use
     * a negative value for no limit.
     *
     * @param maxTotal The cap on the total number of object instances managed
     *                 by the pool. Negative values mean that there is no limit
     *                 to the number of objects allocated by the pool.
     * @see #getMaxTotal
     */
    public final void setMaxTotal(final int maxTotal) {
        this.maxTotal = maxTotal;
    }

    /**
     * Sets the maximum duration the
     * {@code borrowObject()} method should block before throwing an
     * exception when the pool is exhausted and
     * {@link #getBlockWhenExhausted} is true. When less than 0, the
     * {@code borrowObject()} method may block indefinitely.
     *
     * @param maxWaitDuration the maximum duration
     *                        {@code borrowObject()} will block or negative
     *                        for indefinitely.
     * @see #getMaxWaitDuration
     * @see #setBlockWhenExhausted
     * @since 2.11.0
     */
    public final void setMaxWait(final Duration maxWaitDuration) {
        this.maxWaitDuration = PoolImplUtils.nonNull(maxWaitDuration, BaseObjectPoolConfig.DEFAULT_MAX_WAIT);
    }

    /**
     * Sets the maximum amount of time (in milliseconds) the
     * {@code borrowObject()} method should block before throwing an
     * exception when the pool is exhausted and
     * {@link #getBlockWhenExhausted} is true. When less than 0, the
     * {@code borrowObject()} method may block indefinitely.
     *
     * @param maxWaitMillis the maximum number of milliseconds
     *                      {@code borrowObject()} will block or negative
     *                      for indefinitely.
     * @see #getMaxWaitDuration
     * @see #setBlockWhenExhausted
     * @deprecated Use {@link #setMaxWait}.
     */
    @Deprecated
    public final void setMaxWaitMillis(final long maxWaitMillis) {
        setMaxWait(Duration.ofMillis(maxWaitMillis));
    }

    /**
     * Sets whether to include statistics in exception messages.
     * <p>
     * Statistics may not accurately reflect snapshot state at the time of the exception because we do not want to lock the pool when gathering this
     * information.
     * </p>
     *
     * @param messagesDetails whether to include statistics in exception messages.
     * @since 2.11.0
     */
    public void setMessagesStatistics(final boolean messagesDetails) {
        this.messageStatistics = messagesDetails;
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @param minEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                             before it is eligible for eviction
     * @see #getMinEvictableIdleTime
     * @see #setTimeBetweenEvictionRuns
     * @since 2.11.0
     * @deprecated Use {@link #setMinEvictableIdleDuration(Duration)}.
     */
    @Deprecated
    public final void setMinEvictableIdle(final Duration minEvictableIdleTime) {
        this.minEvictableIdleDuration = PoolImplUtils.nonNull(minEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @param minEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                             before it is eligible for eviction
     * @see #getMinEvictableIdleTime
     * @see #setTimeBetweenEvictionRuns
     * @since 2.12.0
     */
    public final void setMinEvictableIdleDuration(final Duration minEvictableIdleTime) {
        this.minEvictableIdleDuration = PoolImplUtils.nonNull(minEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @param minEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                             before it is eligible for eviction
     * @see #getMinEvictableIdleTime
     * @see #setTimeBetweenEvictionRuns
     * @since 2.10.0
     * @deprecated Use {@link #setMinEvictableIdleDuration(Duration)}.
     */
    @Deprecated
    public final void setMinEvictableIdleTime(final Duration minEvictableIdleTime) {
        this.minEvictableIdleDuration = PoolImplUtils.nonNull(minEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive,
     * no objects will be evicted from the pool due to idle time alone.
     *
     * @param minEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool
     *                                   before it is eligible for eviction
     * @see #getMinEvictableIdleTimeMillis
     * @see #setTimeBetweenEvictionRunsMillis
     * @deprecated Use {@link #setMinEvictableIdleTime(Duration)}.
     */
    @Deprecated
    public final void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
        setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
    }

    /**
     * Sets the maximum number of objects to examine during each run (if any)
     * of the idle object evictor thread. When positive, the number of tests
     * performed for a run will be the minimum of the configured value and the
     * number of idle instances in the pool. When negative, the number of tests
     * performed will be <code>ceil({@link #getNumIdle}/
     * abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the
     * value is {@code -n} roughly one nth of the idle objects will be
     * tested per run.
     *
     * @param numTestsPerEvictionRun max number of objects to examine during each evictor run
     * @see #getNumTestsPerEvictionRun
     * @see #setTimeBetweenEvictionRunsMillis
     */
    public final void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTime} (that is, if
     * {@link #getMinEvictableIdleTime} is positive, then
     * {@link #getSoftMinEvictableIdleTime} is ignored).
     *
     * @param softMinEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                                 before it is eligible for eviction if minIdle instances are
     *                                 available
     * @see #getSoftMinEvictableIdleTimeMillis
     * @since 2.11.0
     * @deprecated Use {@link #setSoftMinEvictableIdleDuration(Duration)}.
     */
    @Deprecated
    public final void setSoftMinEvictableIdle(final Duration softMinEvictableIdleTime) {
        this.softMinEvictableIdleDuration = PoolImplUtils.nonNull(softMinEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTime} (that is, if
     * {@link #getMinEvictableIdleTime} is positive, then
     * {@link #getSoftMinEvictableIdleTime} is ignored).
     *
     * @param softMinEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                                 before it is eligible for eviction if minIdle instances are
     *                                 available
     * @see #getSoftMinEvictableIdleTimeMillis
     * @since 2.12.0
     */
    public final void setSoftMinEvictableIdleDuration(final Duration softMinEvictableIdleTime) {
        this.softMinEvictableIdleDuration = PoolImplUtils.nonNull(softMinEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setDurationBetweenEvictionRuns(Duration)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTime} (that is, if
     * {@link #getMinEvictableIdleTime} is positive, then
     * {@link #getSoftMinEvictableIdleTime} is ignored).
     *
     * @param softMinEvictableIdleTime minimum amount of time an object may sit idle in the pool
     *                                 before it is eligible for eviction if minIdle instances are
     *                                 available
     * @see #getSoftMinEvictableIdleTimeMillis
     * @since 2.10.0
     * @deprecated Use {@link #setSoftMinEvictableIdleDuration(Duration)}.
     */
    @Deprecated
    public final void setSoftMinEvictableIdleTime(final Duration softMinEvictableIdleTime) {
        this.softMinEvictableIdleDuration = PoolImplUtils.nonNull(softMinEvictableIdleTime, BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION);
    }

    /**
     * Sets the minimum amount of time an object may sit idle in the pool
     * before it is eligible for eviction by the idle object evictor (if any -
     * see {@link #setTimeBetweenEvictionRunsMillis(long)}),
     * with the extra condition that at least {@code minIdle} object
     * instances remain in the pool. This setting is overridden by
     * {@link #getMinEvictableIdleTimeMillis} (that is, if
     * {@link #getMinEvictableIdleTimeMillis} is positive, then
     * {@link #getSoftMinEvictableIdleTimeMillis} is ignored).
     *
     * @param softMinEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool
     *                                       before it is eligible for eviction if minIdle instances are
     *                                       available
     * @see #getSoftMinEvictableIdleTimeMillis
     * @deprecated Use {@link #setSoftMinEvictableIdleDuration(Duration)}.
     */
    @Deprecated
    public final void setSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
        setSoftMinEvictableIdleTime(Duration.ofMillis(softMinEvictableIdleTimeMillis));
    }

    /**
     * Sets the listener used (if any) to receive notifications of exceptions
     * unavoidably swallowed by the pool.
     *
     * @param swallowedExceptionListener The listener or {@code null}
     *                                   for no listener
     */
    public final void setSwallowedExceptionListener(
            final SwallowedExceptionListener swallowedExceptionListener) {
        this.swallowedExceptionListener = swallowedExceptionListener;
    }

    /**
     * Sets whether objects borrowed from the pool will be validated before
     * being returned from the {@code borrowObject()} method. Validation is
     * performed by the {@code validateObject()} method of the factory
     * associated with the pool. If the object fails to validate, it will be
     * removed from the pool and destroyed, and a new attempt will be made to
     * borrow an object from the pool.
     *
     * @param testOnBorrow {@code true} if objects should be validated
     *                     before being returned from the
     *                     {@code borrowObject()} method
     * @see #getTestOnBorrow
     */
    public final void setTestOnBorrow(final boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    /**
     * Sets whether objects created for the pool will be validated before
     * being returned from the {@code borrowObject()} method. Validation is
     * performed by the {@code validateObject()} method of the factory
     * associated with the pool. If the object fails to validate, then
     * {@code borrowObject()} will fail.
     *
     * @param testOnCreate {@code true} if newly created objects should be
     *                     validated before being returned from the
     *                     {@code borrowObject()} method
     * @see #getTestOnCreate
     * @since 2.2
     */
    public final void setTestOnCreate(final boolean testOnCreate) {
        this.testOnCreate = testOnCreate;
    }

    /**
     * Sets whether objects borrowed from the pool will be validated when
     * they are returned to the pool via the {@code returnObject()} method.
     * Validation is performed by the {@code validateObject()} method of
     * the factory associated with the pool. Returning objects that fail validation
     * are destroyed rather then being returned the pool.
     *
     * @param testOnReturn {@code true} if objects are validated on
     *                     return to the pool via the
     *                     {@code returnObject()} method
     * @see #getTestOnReturn
     */
    public final void setTestOnReturn(final boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    /**
     * Sets whether objects sitting idle in the pool will be validated by the
     * idle object evictor (if any - see
     * {@link #setDurationBetweenEvictionRuns(Duration)}). Validation is performed
     * by the {@code validateObject()} method of the factory associated
     * with the pool. If the object fails to validate, it will be removed from
     * the pool and destroyed.  Note that setting this property has no effect
     * unless the idle object evictor is enabled by setting
     * {@code timeBetweenEvictionRunsMillis} to a positive value.
     *
     * @param testWhileIdle {@code true} so objects will be validated by the evictor
     * @see #getTestWhileIdle
     * @see #setTimeBetweenEvictionRuns
     */
    public final void setTestWhileIdle(final boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }

    /**
     * Sets the number of milliseconds to sleep between runs of the idle object evictor thread.
     * <ul>
     * <li>When positive, the idle object evictor thread starts.</li>
     * <li>When non-positive, no idle object evictor thread runs.</li>
     * </ul>
     *
     * @param timeBetweenEvictionRuns duration to sleep between evictor runs
     * @see #getDurationBetweenEvictionRuns()
     * @since 2.10.0
     * @deprecated Use {@link #setDurationBetweenEvictionRuns(Duration)}.
     */
    @Deprecated
    public final void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) {
        this.durationBetweenEvictionRuns = PoolImplUtils.nonNull(timeBetweenEvictionRuns, BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS);
        startEvictor(this.durationBetweenEvictionRuns);
    }

    /**
     * Sets the number of milliseconds to sleep between runs of the idle object evictor thread.
     * <ul>
     * <li>When positive, the idle object evictor thread starts.</li>
     * <li>When non-positive, no idle object evictor thread runs.</li>
     * </ul>
     *
     * @param timeBetweenEvictionRunsMillis number of milliseconds to sleep between evictor runs
     * @see #getDurationBetweenEvictionRuns()
     * @deprecated Use {@link #setDurationBetweenEvictionRuns(Duration)}.
     */
    @Deprecated
    public final void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
        setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
    }

    /**
     * 以给定的延迟启动驱逐程序。如果调用此方法时有一个驱逐程序正在运行，则会停止该程序，并用具有指定延迟的新驱逐程序替换
     * 这个方法需要是最终的，因为它是从构造函数调用的。
     *
     * @param delay 启动前和逐出运行之间的时间（以毫秒为单位）
     */
    final void startEvictor(final Duration delay) {
        synchronized (evictionLock) {
            final boolean isPositiverDelay = PoolImplUtils.isPositive(delay);
            if (evictor == null) { // Starting evictor for the first time or after a cancel
                if (isPositiverDelay) { // Starting new evictor
                    evictor = new Evictor();
                    EvictionTimer.schedule(evictor, delay, delay);
                }
            } else if (isPositiverDelay) { // Stop or restart of existing evictor: Restart
                synchronized (EvictionTimer.class) { // Ensure no cancel can happen between cancel / schedule calls
                    EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, true);
                    evictor = null;
                    evictionIterator = null;
                    evictor = new Evictor();
                    EvictionTimer.schedule(evictor, delay, delay);
                }
            } else { // Stopping evictor
                EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false);
            }
        }
    }

    /**
     * Stops the evictor.
     */
    void stopEvictor() {
        startEvictor(Duration.ofMillis(-1L));
    }

    /**
     * Swallows an exception and notifies the configured listener for swallowed
     * exceptions queue.
     *
     * @param swallowException exception to be swallowed
     */
    final void swallowException(final Exception swallowException) {
        final SwallowedExceptionListener listener = getSwallowedExceptionListener();

        if (listener == null) {
            return;
        }

        try {
            listener.onSwallowException(swallowException);
        } catch (final VirtualMachineError e) {
            throw e;
        } catch (final Throwable ignored) {
            // Ignore. Enjoy the irony.
        }
    }

    @Override
    protected void toStringAppendFields(final StringBuilder builder) {
        builder.append("maxTotal=");
        builder.append(maxTotal);
        builder.append(", blockWhenExhausted=");
        builder.append(blockWhenExhausted);
        builder.append(", maxWaitDuration=");
        builder.append(maxWaitDuration);
        builder.append(", lifo=");
        builder.append(lifo);
        builder.append(", fairness=");
        builder.append(fairness);
        builder.append(", testOnCreate=");
        builder.append(testOnCreate);
        builder.append(", testOnBorrow=");
        builder.append(testOnBorrow);
        builder.append(", testOnReturn=");
        builder.append(testOnReturn);
        builder.append(", testWhileIdle=");
        builder.append(testWhileIdle);
        builder.append(", durationBetweenEvictionRuns=");
        builder.append(durationBetweenEvictionRuns);
        builder.append(", numTestsPerEvictionRun=");
        builder.append(numTestsPerEvictionRun);
        builder.append(", minEvictableIdleTimeDuration=");
        builder.append(minEvictableIdleDuration);
        builder.append(", softMinEvictableIdleTimeDuration=");
        builder.append(softMinEvictableIdleDuration);
        builder.append(", evictionPolicy=");
        builder.append(evictionPolicy);
        builder.append(", closeLock=");
        builder.append(closeLock);
        builder.append(", closed=");
        builder.append(closed);
        builder.append(", evictionLock=");
        builder.append(evictionLock);
        builder.append(", evictor=");
        builder.append(evictor);
        builder.append(", evictionIterator=");
        builder.append(evictionIterator);
        builder.append(", factoryClassLoader=");
        builder.append(factoryClassLoader);
        builder.append(", oname=");
        builder.append(objectName);
        builder.append(", creationStackTrace=");
        builder.append(creationStackTrace);
        builder.append(", borrowedCount=");
        builder.append(borrowedCount);
        builder.append(", returnedCount=");
        builder.append(returnedCount);
        builder.append(", createdCount=");
        builder.append(createdCount);
        builder.append(", destroyedCount=");
        builder.append(destroyedCount);
        builder.append(", destroyedByEvictorCount=");
        builder.append(destroyedByEvictorCount);
        builder.append(", destroyedByBorrowValidationCount=");
        builder.append(destroyedByBorrowValidationCount);
        builder.append(", activeTimes=");
        builder.append(activeTimes);
        builder.append(", idleTimes=");
        builder.append(idleTimes);
        builder.append(", waitTimes=");
        builder.append(waitTimes);
        builder.append(", maxBorrowWaitDuration=");
        builder.append(maxBorrowWaitDuration);
        builder.append(", swallowedExceptionListener=");
        builder.append(swallowedExceptionListener);
    }

    /**
     * 从池中借用对象后更新统计信息
     *
     * @param p            object borrowed from the pool
     * @param waitDuration that the borrowing thread had to wait
     */
    final void updateStatsBorrow(final PooledObject<T> p, final Duration waitDuration) {
        //  当有新的请求需要借用对象时，如果对象池中的对象数量已经达到了最大值，那么就需要选择一个或多个空闲对象进行回收，以便为新的请求腾出空间。此时，会将借用出去的对象数量加一，并将该对象标记为已借用状态。
        borrowedCount.incrementAndGet();
        idleTimes.add(p.getIdleDuration());
        waitTimes.add(waitDuration);

        // lock-free optimistic-locking maximum
        Duration currentMaxDuration;
        do {
            currentMaxDuration = maxBorrowWaitDuration.get();
            if (currentMaxDuration.compareTo(waitDuration) >= 0) {
                break;
            }
        } while (!maxBorrowWaitDuration.compareAndSet(currentMaxDuration, waitDuration));
    }

    /**
     * Updates statistics after an object is returned to the pool.
     *
     * @param activeTime the amount of time (in milliseconds) that the returning
     *                   object was checked out
     */
    final void updateStatsReturn(final Duration activeTime) {
        returnedCount.incrementAndGet();
        activeTimes.add(activeTime);
    }

}
